/*
 * xe_form.c - xe form related
 * Clunix, cezanne@clunix.com, 2005.9
 *
 * Copyright (c) 2001-2005 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "eds.h"

static ecm_mutex_t		lock_xeform;

static ECM_INLINE sr_t *
find_xe_form(srt_t srt)
{
	sr_t	*sr_xe;

	SR_FOREACH_CHILD(sr_eds_xe_forms, NULL, sr_xe) {
		if (sr_get_type(sr_xe) == srt)
			return sr_xe;
	}
	return NULL;
}

void
eds_add_xe_forms(sr_t *sr_forms)
{
	ecm_mutex_lock(lock_xeform);

	sr_forms = sr_dup(sr_forms);
	sr_merge(sr_eds_xe_forms, sr_forms);
	sr_free(sr_forms);

	ecm_mutex_unlock(lock_xeform);
}

void
eds_del_xe_forms(sr_t *sr_forms)
{
	sr_t	*sr_form, *sr_xe_form;
	srt_t	xe_srt;

	ecm_mutex_lock(lock_xeform);

	SR_FOREACH_CHILD(sr_forms, NULL, sr_form) {
		xe_srt = sr_get_type(sr_form);
		sr_xe_form = find_xe_form(xe_srt);
		sr_free(sr_xe_form);
	}

	ecm_mutex_unlock(lock_xeform);
}

sr_t *
eds_get_xe_form(srt_t srt)
{
	sr_t	*sr_xe, *sr_xe_form;

	ecm_mutex_lock(lock_xeform);
	sr_xe_form = find_xe_form(srt);
	sr_xe = sr_dup(sr_xe_form);
	ecm_mutex_unlock(lock_xeform);

	return sr_xe;
}

sr_t *
eds_get_xe_forms(void)
{
	sr_t	*sr_xe_forms;

	ecm_mutex_lock(lock_xeform);
	sr_xe_forms = sr_dup(sr_eds_xe_forms);
	ecm_mutex_unlock(lock_xeform);

	return sr_xe_forms;
}

static ECM_INLINE ECMBOOL
check_xe_srt(srt_t srt)
{
	sr_t	*sr_xe_form;

	ecm_mutex_lock(lock_xeform);
	sr_xe_form = find_xe_form(srt);
	ecm_mutex_unlock(lock_xeform);

	return (sr_xe_form != NULL);
}

ECMBOOL
eds_is_xe_srt(srt_t srt)
{
	return check_xe_srt(srt);
}

ECMBOOL
eds_is_xe_sr(const sr_t *sr)
{
	if (sr == NULL)
		return ECM_FALSE;
	return (sr_is_HMS(sr->parent) && check_xe_srt(sr->type));
}

static srt_t do_get_srt(srt_t srt, const sr_t *sr_LNI, ECMBOOL *pis_vector);

static srt_t
do_get_srt_with_sub(srt_t srt, const sr_t *sr_LNI, ECMBOOL *pis_vector)
{
	srt_t		srt_res;
	ECMBOOL		is_vector;

	is_vector = ECM_FALSE;
	srt_res = do_get_srt(srt, sr_LNI, &is_vector);
	if (srt_res == ST_NULL) {
		const sr_type_t	*srtinfo;

		srtinfo = srt_get_info(srt);
		if (srtinfo->srts_sub) {
			srt_t	*psrt;

			for (psrt = srtinfo->srts_sub; *psrt != ST_NULL; psrt++) {
				is_vector = ECM_FALSE;
				srt_res = do_get_srt(*psrt, sr_LNI, &is_vector);
				if (srt_res != ST_NULL)
					break;
			}
		}
	}
	if (srt_res != ST_NULL && is_vector)
		*pis_vector = ECM_TRUE;
	return srt_res;
}

static srt_t
do_get_srt_with_xeform(srt_t srt, const sr_t *sr_LNI, ECMBOOL *pis_vector)
{
	sr_t	*sr_form, *sr_sub_form;
	const char	*name;

	sr_form = find_xe_form(srt);
	if (sr_form == NULL)
		return ST_NULL;

again:
	name = sr_getc_str(sr_LNI, "Name");
	sr_sub_form = sr_getc(sr_form, name);
	if (sr_sub_form == NULL)
		return ST_NULL;
	if (sr_get_type(sr_sub_form) == ST_DIR) {
		sr_LNI = sr_next(sr_LNI);
		sr_form = sr_sub_form;
		goto again;
	}
	return do_get_srt(sr_get_type(sr_sub_form), sr_next(sr_LNI), pis_vector);
}

static srt_t
do_get_srt(srt_t srt, const sr_t *sr_LNI, ECMBOOL *pis_vector)
{
	LNI_type_t	type;

	if (sr_LNI == NULL)
		return srt;

	if (srt_is_ref(srt))
		return do_get_srt_with_sub(srt_get_target_type(srt), sr_LNI, pis_vector);

	type = sr_getc_int(sr_LNI, "Type");
	switch (type) {
	case LNI_TYPE_ALL:
	case LNI_TYPE_ANY:
		if ((srt = srt_get_member_type(srt)) == ST_NULL)
			return ST_NULL;
		if (type == LNI_TYPE_ALL)
			*pis_vector = ECM_TRUE;
		return do_get_srt_with_sub(srt, sr_next(sr_LNI), pis_vector);
	case LNI_TYPE_NAME: {
		srt_t	srt_mbr;

		if ((srt_mbr = srt_get_member_type(srt)) != ST_NULL) {
			return do_get_srt_with_sub(srt_mbr, sr_next(sr_LNI), pis_vector);
		}
		return do_get_srt_with_xeform(srt, sr_LNI, pis_vector);
	}
	default:
		break;
	}
	return ST_NULL;
}

srt_t
eds_get_srt(srt_t srt, const sr_t *sr_LNP)
{
	sr_t	*sr_LNI;
	ECMBOOL	is_vector = ECM_FALSE;

	if (srt == ST_NULL || sr_get_type(sr_LNP) != ST_LNP)
		return ST_NULL;
	if (is_ABS_sr_LNP(sr_LNP))
		return ST_NULL;

	sr_LNI = sr_first(sr_getc(sr_LNP, "LNIs"));
	if (srt_is_vHMS(srt)) {
		LNI_type_t	type;

		is_vector = ECM_TRUE;
		type = sr_getc_int(sr_LNI, "Type");
		if (type == LNI_TYPE_ALL || type == LNI_TYPE_ANY)
			sr_LNI = sr_next(sr_LNI);
		srt = srt_get_member_type(srt);
	}
	else
		srt = srt_get_real_type(srt);

	srt = do_get_srt(srt, sr_LNI, &is_vector);

	if (srt != ST_NULL) {
		if (is_ref_sr_LNP(sr_LNP)) {
			if (!srt_is_ref(srt))
				return ST_NULL;
		}
		else if (srt_is_ref(srt))
			srt = srt_get_target_type(srt);
		if (is_vector)
			srt = srt_get_vHMS_type(srt);
	}

	return srt;
}

sr_t *
eds_xe_create(srt_t srt)
{
	sr_t	*sr_xe;

	ECMASSERT(srt_is_real(srt));

	sr_xe = eds_get_xe_form(srt);
	if (sr_xe == NULL)
		sr_xe = sr_create(srt);
	return sr_xe;
}

void
eds_xeform_init(void)
{
	lock_xeform = ecm_mutex_create(ECM_FALSE);
}

/////////
static int
get_tail_random_count(const char *name)
{
	const char	*p;
	int	n_tail_rand = 0;

	if (ecm_empty_str(name))
		return 0;
	p = name;
	while (*p != '\0') {
		if (*p++ == '?') {
			n_tail_rand++;
		}
		else
			n_tail_rand = 0;
	}
	return n_tail_rand;
}

char *
eds_build_xe_name(const char *name, ECMBOOL *panony)
{
	int	n_tail_rand;

	if (ecm_empty_str(name)) {
		*panony = ECM_TRUE;
		return ecm_get_randstring("anony", 5);
	}

	n_tail_rand = get_tail_random_count(name);
	if (n_tail_rand == 0) {
		*panony = ECM_FALSE;
		return ecm_strdup(name);
	}
	else {
		char	*unique_name, *prefix;
		int	len = ecm_strlen(name) - n_tail_rand;

		ecm_amemcpy((ecm_ptr_t *)&prefix, name, len + 1);
		prefix[len] = '\0';
		unique_name = ecm_get_randstring(prefix, n_tail_rand);
		ecm_free(prefix);

		*panony = ECM_TRUE;
		return unique_name;
	}
}

static char *
create_xe_name(sr_t *sr_HMS, const char *c_name)
{
	char	*name;
	ECMBOOL	anony;

again:
	name = eds_build_xe_name(c_name, &anony);
	if (sr_getc(sr_HMS, name)) {
		ecm_free(name);
		if (anony)
			goto again;
		return NULL;
	}
	return name;
}

sr_t *
eds_xecreate(sr_t *sr_HMS, const char *name, srt_t srt, ECMBOOL isolated)
{
	sr_t	*sr_xe;
	char	*unique_name;
	srt_t	member_srt;

	member_srt = sr_get_member_type(sr_HMS);
	if (member_srt == ST_NULL)
		return NULL;
	if (srt != ST_NULL) {
		if (!srt_is_member(sr_get_type(sr_HMS), srt))
			return NULL;
		member_srt = srt;
	}

	unique_name = create_xe_name(sr_HMS, name);
	if (unique_name == NULL)
		return NULL;

	sr_xe = eds_get_xe_form(member_srt);
	if (sr_xe == NULL)
		sr_xe = sr_create(member_srt);

	sr_set_name(sr_xe, unique_name);
	if (!isolated)
		sr_append(sr_HMS, sr_xe);

	ecm_free(unique_name);
	return sr_xe;
}

